#include "HashingStrategy.hpp"

#include <stdint.h>
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <exception>
#include <stdexcept> 

HashingStrategy::HashingStrategy (size_t hash_length)
{
    this->stm = PROCESSING;
    this->digest_length = hash_length;
    this->digest = new uint8_t[hash_length];
}

HashingStrategy::~HashingStrategy (void)
{
    delete[] this->digest;
}

void HashingStrategy::next_state (void)
{
    this->stm = FINISHED;
}

void HashingStrategy::set_corrupted (void)
{
    this->stm = CORRUPTED;
}

bool HashingStrategy::is_corrupted (void) const
{
    return (this->stm == CORRUPTED) ? true : false;
}

bool HashingStrategy::final_state (void) const
{
    return (this->stm == FINISHED) ? true : false ;
}

void HashingStrategy::set_digest (uint8_t* array, size_t length)
{
    if (array == NULL) {
        throw std::invalid_argument ("HashingStrategy : array parameter is a null pointer");
    } else if (length != this->digest_length) {
        throw std::invalid_argument ("HashingStrategy : provided length does not correspond to digest length");
    }

    std::memcpy (this->digest, array, this->digest_length);
}

void HashingStrategy::get_digest (uint8_t* array, size_t length) const
{
    if( !this->final_state() ) {
        throw std::logic_error("HashingStrategy : you did not finalize the hashing process, finalize() must be called before");
    }

    if (array == NULL) {
        throw std::invalid_argument ("HashingStrategy : array parameter is a null pointer");
    } else if (length != this->digest_length) {
        throw std::invalid_argument ("HashingStrategy : provided length does not correspond to digest length");
    }

    std::memcpy (array, this->digest, this->digest_length);
}

std::string HashingStrategy::to_string (void) const
{
    if( !this->final_state() ) {
        throw std::logic_error("HashingStrategy : you did not finalize the hashing process, finalize() must be called before");
    }

    char printable_digest[2*this->digest_length + 1];

    for(size_t i=0; i<this->digest_length; ++i) {
        std::snprintf (printable_digest + 2*i, 3, "%02x", this->digest[i]);
    }

    std::string s(printable_digest);
    return s;
}

std::ostream& operator<< (std::ostream& os, const HashingStrategy &h)
{
    os << h.to_string();
    return os;
}
